package models;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.text.format.Time;

/**
 * DataSource is a Singleton class that interfaces between the database and Android Activities.
 * This class is used to store, edit and delete Task information in the Database.
 * It is also used to retrieve task information from the database and create task objects with that information.
 * @author Nikhil Karkarey and Abdelelah Salama
 *
 */
public class DataSource {
	
	// Database fields
	private static DataSource instance;
	private SQLiteDatabase database;
	private DbHelper dbHelper;
	private String[] allTasksColumns = { DbHelper.COLUMN_ID,DbHelper.COLUMN_TITLE,
	DbHelper.COLUMN_STARTTIME, DbHelper.COLUMN_DURATION, DbHelper.COLUMN_TRAVELTIME,
	DbHelper.COLUMN_PRIORITY, DbHelper.COLUMN_STATUS};
	
	// private constructor
	private DataSource(Context context) {
		
		dbHelper = new DbHelper(context);
	}
	
	/**
	 * Returns the only existing instance of DataSource
	 * @param context
	 * @return DataSource instance in use, or returns a new instance if one is not already instantiated
	 */
	public static DataSource getInstance(Context context){
		if(instance == null){
			instance = new DataSource(context);
		}
		return instance;
	}
	
	/**
	 * Opens the database for operations
	 * @throws SQLException
	 */
	public void open() throws SQLException {
		database = dbHelper.getWritableDatabase();
	}
	
	/**
	 * Closes the database for operations
	 */
	public void close() {
		dbHelper.close();
	}

	/**
	 * Creates and returns a task object and stores its information in a PLANNINGTASK table
	 * @param title
	 * @param startTime
	 * @param duration
	 * @param travelTime
	 * @param priority
	 * @param status
	 * @return Task object
	 */
	public Task createPlanningTask(String title, String startTime, int duration, int travelTime, int priority, int status  ) {
		ContentValues values = new ContentValues();
		values.put(DbHelper.COLUMN_TITLE, title);
		values.put(DbHelper.COLUMN_STARTTIME, startTime);
		values.put(DbHelper.COLUMN_DURATION, duration);
		values.put(DbHelper.COLUMN_TRAVELTIME, travelTime);
		values.put(DbHelper.COLUMN_PRIORITY, priority);
		values.put(DbHelper.COLUMN_STATUS, status);
		
		long taskId = database.insert(DbHelper.TABLE_PLANNINGTASKS, null, values);
		Cursor cursor = database.query(DbHelper.TABLE_PLANNINGTASKS,
				allTasksColumns, DbHelper.COLUMN_ID + " = " + taskId, null,null, null, null);
		cursor.moveToFirst();
		Task task = cursorToTask(cursor);
		cursor.close();
		return task;
	}
	
	/**
	 * Enters a task in TABLE_SCHEDULE
	 * @param id
	 * @param title
	 * @param startTime
	 * @param duration
	 * @param travelTime
	 * @param priority
	 * @param status
	 */
	public void saveScheduleTasks(Collection<Task> tasks) {
		
		for(Task t : tasks) {
			
			long taskId = t.getID();
			String title = t.getTitle();
			String startTime = parseTime(t.getStartTime());
			int duration = t.getEstimatedDuration();
			int travelTime = t.getEstimatedTravel();
			int priority = t.getPriority();
			int status = 0;
			
			ContentValues values = new ContentValues();
			values.put(DbHelper.COLUMN_ID, taskId);
			values.put(DbHelper.COLUMN_TITLE, title);
			values.put(DbHelper.COLUMN_STARTTIME, startTime);
			values.put(DbHelper.COLUMN_DURATION, duration);
			values.put(DbHelper.COLUMN_TRAVELTIME, travelTime);
			values.put(DbHelper.COLUMN_PRIORITY, priority);
			values.put(DbHelper.COLUMN_STATUS, status);
			
			database.insert(DbHelper.TABLE_SCHEDULE, null, values);
		}
		
	}
	
	/**
	 * Enters a task in TABLE_PREVIEW
	 * @param id
	 * @param title
	 * @param startTime
	 * @param duration
	 * @param travelTime
	 * @param priority
	 * @param status
	 */
	public void savePreviewTasks(Collection<Task> tasks) {
		
		for(Task t : tasks) {
			long taskId = t.getID();
			String title = t.getTitle();
			String startTime = parseTime(t.getStartTime());
			int duration = t.getEstimatedDuration();
			int travelTime = t.getEstimatedTravel();
			int priority = t.getPriority();
			int status = 0;
			
			ContentValues values = new ContentValues();
			values.put(DbHelper.COLUMN_ID, taskId);
			values.put(DbHelper.COLUMN_TITLE, title);
			values.put(DbHelper.COLUMN_STARTTIME, startTime);
			values.put(DbHelper.COLUMN_DURATION, duration);
			values.put(DbHelper.COLUMN_TRAVELTIME, travelTime);
			values.put(DbHelper.COLUMN_PRIORITY, priority);
			values.put(DbHelper.COLUMN_STATUS, status);
			
			database.insert(DbHelper.TABLE_PREVIEW, null, values);
		}
	}
	
	/**
	 * Enters a task in TABLE_REGENERATE
	 * @param id
	 * @param title
	 * @param startTime
	 * @param duration
	 * @param travelTime
	 * @param priority
	 * @param status
	 */
	public void saveRegenerateTasks(Collection<Task> tasks) {
		
		for(Task t : tasks) {
			long taskId = t.getID();
			String title = t.getTitle();
			String startTime;
			if(t.getStartTime() == null) {
                    startTime = "Not Set";
			}
			else {
				startTime = parseTime(t.getStartTime());
			}
			int duration = t.getEstimatedDuration();
			int travelTime = t.getEstimatedTravel();
			int priority = t.getPriority();
			int status = 0;
			
			ContentValues values = new ContentValues();
			values.put(DbHelper.COLUMN_ID, taskId);
			values.put(DbHelper.COLUMN_TITLE, title);
			values.put(DbHelper.COLUMN_STARTTIME, startTime);
			values.put(DbHelper.COLUMN_DURATION, duration);
			values.put(DbHelper.COLUMN_TRAVELTIME, travelTime);
			values.put(DbHelper.COLUMN_PRIORITY, priority);
			values.put(DbHelper.COLUMN_STATUS, status);
			
			database.insert(DbHelper.TABLE_REGENERATE, null, values);
		}
	}
	
	/**
	 * Edits a task object and updates its information in a PLANNINGTASK table
	 * @param taskId
	 * @param title
	 * @param startTime
	 * @param duration
	 * @param travelTime
	 * @param priority
	 * @param status
	 */
	public void editPlanningTask(long taskId, String title, String startTime, int duration, int travelTime, int priority, int status  ) {
		ContentValues values = new ContentValues();
		values.put(DbHelper.COLUMN_TITLE, title);
		values.put(DbHelper.COLUMN_STARTTIME, startTime);
		values.put(DbHelper.COLUMN_DURATION, duration);
		values.put(DbHelper.COLUMN_TRAVELTIME, travelTime);
		values.put(DbHelper.COLUMN_PRIORITY, priority);
		values.put(DbHelper.COLUMN_STATUS, status);
		
		database.update(DbHelper.TABLE_PLANNINGTASKS, values, DbHelper.COLUMN_ID + " = " + taskId, null);
	}
	
	/**
	 * Edits a schedule task status: completed/uncompleted
	 * @param status
	 */
	public void editScheduleTaskStatus(long taskId, int status) {
		ContentValues values = new ContentValues();
		values.put(DbHelper.COLUMN_STATUS, status);
		
		database.update(DbHelper.TABLE_SCHEDULE, values, DbHelper.COLUMN_ID + " = " + taskId, null);
	}
	
	/**
	 * Deletes a task from PLANNINGTASKS table
	 * @param taskId
	 */
	public void deletePlanningTask(long taskId) {
		database.delete(DbHelper.TABLE_PLANNINGTASKS, DbHelper.COLUMN_ID + " = " + taskId, null);
	}
	
	/**
	 * Deletes all tasks from a given table name
	 * @param tableName
	 */
	public void deleteAllTasks(String tableName) {
		database.delete(tableName, null, null);
	}

	/**
	 * Gets all tasks stored in PLANNINGLIST or SCHEDULE table
	 * @return List<Task> tasks
	 */
	public List<Task> getAllTasks(String tableName) {
		List<Task> tasks = new ArrayList<Task>();
		Cursor cursor;
		if(tableName.equals(DbHelper.TABLE_SCHEDULE) || tableName.equals(DbHelper.TABLE_PREVIEW)) {
			// sort tasks by asc start time only for Schedule Table
			cursor = database.query(tableName,
					allTasksColumns, null, null, null, null, DbHelper.COLUMN_STARTTIME);
		} else {
			cursor = database.query(tableName,
					allTasksColumns, null, null, null, null, null);
		}

		cursor.moveToFirst();
		while (!cursor.isAfterLast()) {
			Task task = cursorToTask(cursor);
			tasks.add(task);
			cursor.moveToNext();
		}
		// Make sure to close the cursor
		cursor.close();
		return tasks;
	}

	
	/**
	 * Gets all tasks that are not completed from Schedule table
	 * @return List<Task> tasks
	 */
	public List<Task> getAllIncompletedTasks() {
		List<Task> tasks = new ArrayList<Task>();
		Cursor cursor = database.query(DbHelper.TABLE_SCHEDULE,
				allTasksColumns, null, null, null, null, DbHelper.COLUMN_STARTTIME);

		cursor.moveToFirst();
		while (!cursor.isAfterLast()) {
			Task task = cursorToTask(cursor);
			if(!task.isCompleted()) {
				tasks.add(task);
			}
			cursor.moveToNext();
		}
		// Make sure to close the cursor
		cursor.close();
		return tasks;
	}
	
	/**
	 * Gets all tasks that are not completed from Schedule table
	 * @return List<Task> tasks
	 */
	public List<Task> getAllCompletedTasks() {
		List<Task> tasks = new ArrayList<Task>();
		Cursor cursor = database.query(DbHelper.TABLE_SCHEDULE,
				allTasksColumns, null, null, null, null, DbHelper.COLUMN_STARTTIME);

		cursor.moveToFirst();
		while (!cursor.isAfterLast()) {
			Task task = cursorToTask(cursor);
			if(task.isCompleted()) {
				tasks.add(task);
			}
			cursor.moveToNext();
		}
		// Make sure to close the cursor
		cursor.close();
		return tasks;
	}
	

	/**
	 * Builds a Task object given a database Cursor
	 * @param cursor
	 * @return Task task
	 */
	private Task cursorToTask(Cursor cursor) {
		long taskId = cursor.getLong(0);

		String title = cursor.getString(1);
		String startTimeString = cursor.getString(2); // have to parse to Time
		int duration = cursor.getInt(3);
		int travelTime = cursor.getInt(4);
		int priority = cursor.getInt(5);
		int statusInteger = cursor.getInt(6);

		// parse string startTime to Time startTime
		Time startTime = new Time();
		if(startTimeString == null) {
			startTime = null;
		} 
		
		else if(startTimeString.equalsIgnoreCase("start time: not set")) {
			startTime = null;
		}
		else if (startTimeString.equalsIgnoreCase("not set")) {
			startTime = null;
		}

		else {
			String[] startTimeValues = startTimeString.split(":");
			if(startTimeValues.length == 3) {
				startTime.set(0, Integer.parseInt(startTimeValues[2]), Integer.parseInt(startTimeValues[1]), Integer.parseInt(startTimeValues[0]), 0, 0);
			}
			else {
				startTime.set(0 , Integer.parseInt(startTimeValues[1]), Integer.parseInt(startTimeValues[0]), 0, 0, 0);
			}
		}
		
		// parse status from integer to boolean
		boolean status;
		if(statusInteger == 0) {
			status = false;
		}
		else {
			status = true;
		}
		// create the task object
		Task task = new Task(taskId, title, startTime, duration, travelTime, priority, status);
		return task;
	}
	
	// parses Time object to display in HH:MM string format
    // @param time Time object to display
    // @return String with time in HH:MM format. 'Time not set' if time is null
    private String parseTime(Time time) {
            if (time == null) {
                    throw new IllegalArgumentException("time is not allowed to be null in a schedule task");
            }
            return time.toString().substring(6,8) + ":" + time.toString().substring(9, 11) + ":" + time.toString().substring(11,13);
    }
}
